import tensorflow as tf
from abc import ABCMeta,abstractmethod
from stock_a.feature.stock_feature_source import StockFeatureSource
from stock_a.feature.global_feature_source import GlobalFeatureSource
from stock_a.feature import feature_pb2

class AbstractFeature(metaclass=ABCMeta):
    def __init__(self, stock_feature_source: StockFeatureSource, global_feature_source: GlobalFeatureSource):
        self.stock_feature_source = stock_feature_source
        self.global_feature_source = global_feature_source

    @abstractmethod
    def feature_name(self):
        """
        特征名
        """
        raise Exception("AbstractFeature#feature_name NotImplementedException")

    @abstractmethod
    def feature_type(self):
        """
        特征类型
        """
        raise Exception("AbstractFeature#feature_type NotImplementedException")

    @abstractmethod
    def feature_shape(self):
        """
        特征shape
        """
        raise Exception("AbstractFeature#feature_shape NotImplementedException")

    @abstractmethod
    def set_pb_feature(self, idx: int, feature_pb: feature_pb2.feature):
        """
        将特征set到pb对应字段，pb序列化后的特征存储于mysql表
        """
        raise Exception("AbstractFeature NotImplementedException")

    @abstractmethod
    def get_pb_feature(self, feature_pb: feature_pb2.feature):
        """
        从pb中获取特征对应的字段
        :param feature_pb:
        :return:
        """
        raise Exception("AbstractFeature#get_pb_feature NotImplementedException")

    def generate_tf_record(self, feature_pb: feature_pb2.feature):
        """
        构造tfrecord，包含特征名和特征value，特征value通过重写get_pb_feature实现
        """
        feature_value = self.get_pb_feature(feature_pb)
        if self.feature_type() == tf.float32:
            return self.feature_name(), tf.train.Feature(float_list=tf.train.FloatList(value=feature_value))
        elif self.feature_type() == tf.int64:
            return self.feature_name(), tf.train.Feature(int64_list=tf.train.Int64List(value=feature_value))
        else:
            raise Exception("AbstractFeature#generate_tf_record unsupported feature_type".format(self.feature_type()))

    def generate_tf_type(self):
        """
        返回tensorflow特征数据类型，在dataset解析tfrecord时会使用
        """
        return tf.io.FixedLenFeature(self.feature_shape(), self.feature_type())